#include "xtimer.h"
#include <Windows.h>
#include <ctime>

namespace xge{
namespace xge_internal{

  String Timer::kNowStr;

  const String& Timer::Now() {
#ifdef XGE_USE_WIDE_CHAR 
    if( kNowStr.empty() ) {
      time_t timer = time(0);
      tm* time = localtime(&timer);
      kNowStr = _wasctime(time);
    }
    return kNowStr;
#else
    if( kNowStr.empty()) {
      time_t timer = time(0);
      tm* time = localtime(&timer);
      kNowStr = asctime(time);
    }
    return kNowStr;
#endif
  }


  bool Timer::BeginTimerCounting( size_t high_resolution /* =kMinMicrosecondPrecision */ ) {
    last_call_timer_ = tick_t();
    XASSERT_SAY(high_resolution!=0,_xT("The high resolution paremeter should be not zero!"));
    if( high_resolution >kMinMicrosecondPrecision ) {
      // no need to do the adjustment
      high_resolution_adjust_ = kNoNeedToAdjust ;
      return true;
    } else {
      // you want increase the timer stuff 
      // Listen :
      // the timeBeginPeriod will affect the WHOLE system ( as MSDN says )
      // so the sleep will become more accurate 
      // what is more , it will affect the CPU to enter the battery saving mode
      // which is definitely affected the laptop and man other resource-saving terminal
      // so WATCH out , when using it.
      high_resolution_adjust_ = high_resolution;
      MMRESULT ret = timeBeginPeriod(high_resolution);
      if( ret != 0 ) {
        // there do exist some error in the MMRESULT ( Windows Multimedia Library )
        // we just say there exist an error
        SYS_LOG(PANIC,false,_xT("WindowsMMLib::timeBeginPeriod failed!\nMM error code:%d\n"),(int)ret);
        high_resolution_adjust_ = kNoNeedToAdjust;
        return false;
      }
      return true;
    }
  }

  Timer::tick_t Timer::TimeDiff( ) {
    if( last_call_timer_ == 0 ) {
      last_call_timer_ = timeGetTime();
      return tick_t(0u);
    }
    // Win32 timer is sucking , including Linux 
    // see http://www.grahamwideman.com/gw/tech/dataacq/wintiming.htm

    // internally we have many choice to get the time
    // like:
    // 1. RDSTC 
    //  this is using internal assembly , however it depends on the CPU
    //  that means on different CPU the result may change
    // 2. QueryPerformanceCounter
    //  this is expensive to call , and in order version OS , it have bugs!
    //  see http://www.virtualdub.org/blog/pivot/entry.php?id=106
    // 3. get system time( timeGetTime , timeGetSystemTime )
    //  using timeBeginPeriod and timeEndPeriod control the precision , however
    //  this may lead to very serious OS schedule panic , since the precision affects
    //  the os scheduler .
      
    //  We use timeGetTime() to retreat time 
    DWORD dwCurrentTime = timeGetTime();
    DWORD dwElapseTime  = (static_cast<DWORD>(dwCurrentTime - last_call_timer_)) ;
    last_call_timer_ = dwCurrentTime;
    return tick_t( (size_t)(dwElapseTime) );
  }

  Timer::~Timer(){
    if( high_resolution_adjust_ != kNoNeedToAdjust ) {
      MMRESULT ret = timeEndPeriod(high_resolution_adjust_);
      if( ret !=0 )
        SYS_LOG(PANIC,false,_xT("WindowsMMLib::timeEndPeriod failed!MM error code:%d."),(int)ret);
    }
  }


  const float Timer::tick_t::kPerSecondTick = 1000.0f;

} // namespace xge_internal
}// namespace xge

